package cn.teaseed.comn;

import cn.hutool.core.date.DateUtil;
import cn.teaseed.view.HomeView;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * WebSocket服务器
 */
@Component
@ServerEndpoint(value = "/{connectId}") // web端用/连接  app端用/{connectId}连接  所以需要监听/** 否则app连接不上
public class WebSocketServer {
    protected static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    // 储存已连接的用户及其标识
    protected static Map<String, Session> connections = Maps.newConcurrentMap();
    // 存储消息关系
    protected static Map<String, String> relations = Maps.newConcurrentMap();
    // 存储定时器
    protected static Map<String, ScheduledFuture<?>> clientTimers = Maps.newConcurrentMap();
    public static final Gson gson = new GsonBuilder().create();
    private static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    @Lazy
    @Resource
    private HomeView homeView;

    @OnOpen
    public void onOpen(Session session) throws IOException {
        String clientId = session.getId();
        connections.put(clientId, session);
        logger.info("新的 WebSocket 连接已建立，标识符为: {}", session.getId());
        // 发送标识符给客户端（格式固定，双方都必须获取才可以进行后续通信：比如浏览器和APP）
        DataMessage da = new DataMessage();
        da.setType("bind");
        da.setClientId(clientId);
        da.setMessage("targetId");
        da.setTargetId("");
        send(da);

        // 启动心跳定时器（如果尚未启动）
        if (clientTimers.isEmpty()) {
            executor = Executors.newScheduledThreadPool(1);
            Runnable task = () -> {
                if (!connections.isEmpty()) {
                    logger.info("关系池大小:" + relations.size() + " 连接池大小:" + connections.size() + " 发送心跳消息：" + DateUtil.date());
                    for (String cid : connections.keySet()) {
                        Session client = connections.get(cid);
                        String targetId = relations.get(cid);
                        if (ObjectUtils.isEmpty(targetId)) {
                            targetId = "";
                        }
                        HeartBeatMessage ht = new HeartBeatMessage(cid, targetId);
                        send(client, ht);
                    }
                }
            };
            ScheduledFuture<?> future = executor.scheduleAtFixedRate(task, 1, 60, TimeUnit.SECONDS);
            clientTimers.put(CorsParam.clientID, future);
        }
    }

    private void send(DataMessage da) {
        Session session = connections.get(da.getClientId());
        send(session, da);
    }

    public void sendNoArg(DataMessage da) {
        String clientId = CorsParam.clientID;
        da.setTargetId(relations.get(clientId));
        da.setClientId(clientId);
        if (connections.isEmpty()) return;
        Session session = connections.get(relations.get(clientId));
        String json = gson.toJson(da);
        session.getAsyncRemote().sendText(json);

        logger.info("下发消息:{}", json);
    }

    private void send(Session session, DataMessage da) {
        session.getAsyncRemote().sendText(gson.toJson(da));
    }

    private void send(Session session, String message) {
        session.getAsyncRemote().sendText(message);
    }

    @OnMessage
    public void onMessage(Session session, String text) {
        logger.info("收到消息：{}", text);
        DataMessage data = null;
        try {
            data = gson.fromJson(text, DataMessage.class);
        } catch (Exception e) {
            // 非JSON数据处理
            DataMessage da = new DataMessage();
            da.setType("msg");
            da.setClientId("");
            da.setTargetId("");
            da.setMessage("403");
            send(session, da);
            return;
        }
        // 非法消息来源拒绝
        if (!connections.containsKey(data.getClientId()) && !connections.containsKey(data.getTargetId())) {
            DataMessage da = new DataMessage();
            da.setType("msg");
            da.setClientId("");
            da.setTargetId("");
            da.setMessage("404");
            send(session, da);
            return;
        }
        if (!ObjectUtils.isEmpty(data.getType()) && !ObjectUtils.isEmpty(data.getClientId()) && !ObjectUtils.isEmpty(data.getMessage()) && !ObjectUtils.isEmpty(data.getTargetId())) {
            // 优先处理绑定关系
            String type = data.getType();
            //服务端ID
            String clientId = data.getClientId();
            //APP客户端ID
            String targetId = data.getTargetId();
            //消息
            String message = data.getMessage();
            switch (type) {
                case "bind": {
                    // 服务器下发绑定关系
                    if (connections.containsKey((targetId))) {
                        // relations的双方都不存在这俩id
                        if (!(relations.containsKey(clientId) || relations.containsKey(targetId)) || relations.containsValue(clientId) || relations.containsValue(targetId)) {
                            relations.put(clientId, targetId);
                            DataMessage sendData = new DataMessage(clientId, targetId);
                            sendData.setType("bind");
                            sendData.setMessage("200");
                            send(session, sendData);
                        } else {
                            DataMessage da = new DataMessage(clientId, targetId);
                            da.setType("bind");
                            da.setMessage("400");
                            send(session, da);
                            return;
                        }
                    } else {
                        DataMessage da = new DataMessage(clientId, targetId);
                        da.setType("bind");
                        da.setMessage("401");
                        send(session, da);
                        return;
                    }
                    break;
                }
                case "msg": {
                    //处理App回传的强度数据
                    if (message.startsWith("strength")) {
                        // {"type":"msg","clientId":"xxxx-xxxxxxxxx-xxxxx-xxxxx-xx","targetId":"xxxx-xxxxxxxxx-xxxxx-xxxxx-xx","message":"strength-11+7+100+35"}
                        String[] split = message.split("-");
                        String[] split1 = split[1].split("\\+");
                        //logger.info("A通道强度:{}", split1[0]);
                        //logger.info("B通道强度:{}", split1[1]);
                        //logger.info("A通道强度上限:{}", split1[2]);
                        //logger.info("B通道强度上限:{}", split1[3]);

                        //设置全局强度上限
                        CorsParam.WaveAStrengthMax = split1[2];
                        CorsParam.WaveBStrengthMax = split1[3];
                    }
                }
                default:
                    // 未定义的普通消息
                    if (relations.containsKey(clientId) && !relations.get(clientId).equals(targetId)) {
                        DataMessage da = new DataMessage(clientId, targetId);
                        da.setType("bind");
                        da.setMessage("402");
                        send(session, da);
                        return;
                    }
                    if (connections.containsKey(clientId)) {
                        Session client = connections.get(clientId);
                        DataMessage da = new DataMessage(clientId, targetId);
                        da.setType(data.getType());
                        da.setMessage(data.getMessage());
                        send(client, da);
                    } else {
                        // 未找到匹配的客户端
                        DataMessage da = new DataMessage(clientId, targetId);
                        da.setType("msg");
                        da.setMessage("404");
                        send(session, da);
                    }
                    break;
            }
        }
    }

    /**
     * 关闭连接
     */
    @OnClose
    public void onClosing(Session session) throws IOException {
        // 连接关闭时，清除对应的 clientId 和 WebSocket 实例
        logger.info("WebSocket 连接已关闭");
        // 遍历 clients Map，找到并删除对应的 clientId 条目
        String clientId = session.getId();
        logger.info("断开的client id:" + clientId);
        connections.remove(clientId); //清除ws客户端
        logger.info("已清除" + clientId + " ,当前size: " + connections.size());
    }

    /**
     * 异常处理
     */
    @OnError
    public void onError(Throwable throwable, Session session) {
        // java.io.IOException: 远程主机强迫关闭了一个现有的连接。 这个错误是由于连接直接被关闭 无需处理 依然会自动回调到onClose关闭
        if (throwable instanceof IOException) {
            logger.error(throwable.getMessage());
            return;
        }
    }

    public static Boolean hasOnlineWs() {
        return !connections.isEmpty();
    }

    public static void closeAllWs() {
        if (!clientTimers.isEmpty()) {
            ScheduledFuture<?> scheduledFuture = clientTimers.get(CorsParam.clientID);
            scheduledFuture.cancel(true);
        }
        executor.shutdown();

        DataMessage da = new DataMessage();
        String clientId = CorsParam.clientID;
        da.setTargetId(relations.get(clientId));
        da.setClientId(clientId);
        da.setType("break");
        da.setMessage("209");
        if (connections.isEmpty()) return;
        Session session = connections.get(relations.get(clientId));
        String json = gson.toJson(da);
        session.getAsyncRemote().sendText(json);
        logger.info("下发消息:{}", json);
    }

}